#include "Socket.h"

const int Socket::SOCKET_TCP = IPPROTO_TCP;
const int Socket::SOCKET_UDP = IPPROTO_UDP;
const int Socket::SOCKET_INVALID_ERROR = -1;
const int Socket::SOCKET_ERROR = 0;

const int Socket::SOCKET_DATA_BUFFER_MAX_SIZE = 1024;
using namespace std;

Socket::Socket( const Socket &socket )
{
	m_isConnected = socket.m_isConnected;
	m_protocol = socket.m_protocol;
	m_socketId = socket.m_socketId;
}

Socket::Socket( int socketId, int protocol )
{
	m_socketId = socketId;
	m_isConnected = true;
	m_protocol = protocol;
}

Socket::Socket( int protocol ) throw ( Exception )
{
	m_protocol = protocol;
	switch( m_protocol )
	{
		case SOCKET_TCP:
			m_socketId = socket( AF_INET, SOCK_STREAM, m_protocol );
			if( m_socketId == SOCKET_INVALID_ERROR )
			{
				throw Exception( "Impossible to create a TCP socket" );
			}
			break;
		case SOCKET_UDP:
			m_socketId = socket( AF_INET, SOCK_DGRAM, m_protocol );
			if( m_socketId == SOCKET_INVALID_ERROR )
			{
				throw Exception( "Impossible to create an UDP socket" );
			}
			break;
	}
}


Socket::~Socket( void )
{
	if( m_isConnected )
	{
		closeSocket();
	}
}

void Socket::closeSocket( void )  throw ( Exception )
{
	if( m_isConnected )
	{
		close( m_socketId );
	}
	else
	{
		throw Exception( "Socket is not connected" );
	}
}

void Socket::bindSocket( int port )  throw ( Exception )
{
	    struct sockaddr_in address;
        address.sin_family		= AF_INET;
        address.sin_port		= htons( port );
        address.sin_addr.s_addr = htonl( INADDR_ANY );

        // Bind for TCP and UDP
        if( bind( m_socketId, ( struct sockaddr * ) &address, sizeof( address ) ) < SOCKET_ERROR )
        {
        	ostringstream myStream;
        	myStream << "Can't bind(...) to the port ";
        	myStream << port;
			throw Exception(  myStream.str() );
        }
}
void Socket::listenSocket(  int maxConnections )  throw ( Exception )
{
        // Listen only for TCP
        if( m_protocol == SOCKET_TCP )
        {
            if( listen( m_socketId, maxConnections ) == SOCKET_INVALID_ERROR )
			{
				throw Exception( "Can't listen(...)" );
			}
		}
		else
		{
			throw Exception("Socket's protocol doesn't support listen(...) operation");
		}

		m_isConnected = true;

}

bool Socket::connectTo( std::string host, int port )  throw ( Exception )
{
		struct sockaddr_in destinationAddress;
		memset(&destinationAddress, 0, sizeof(destinationAddress));  		// Mise à 0 de la structure
		destinationAddress.sin_family = AF_INET;                		// Paquet de type internet
		inet_aton(host.c_str(),&destinationAddress.sin_addr);			// Adresse IP du serveur
		destinationAddress.sin_port = htons( port );   	// sur le port x

        // Listen only for TCP
        if( m_protocol == SOCKET_TCP )
        {
            if( connect( m_socketId, (struct sockaddr *) &destinationAddress, sizeof(destinationAddress)) < SOCKET_ERROR )
			{
				ostringstream myStream;
        		myStream << "Can't connect(...) to ";
        		myStream << host;
        		myStream << ":";
        		myStream << port;
				throw Exception( myStream.str() );
			}
		}
		else
		{
			throw Exception("Socket's protocol doesn't support connect(...) operation");
		}

		m_isConnected = true;

		return m_isConnected;
}

Socket * Socket::acceptConnection( void ) throw ( Exception )
{
	Socket * returnSocket;
	if( ! m_isConnected )
	{
		throw Exception("Socket is not connected");
		return NULL;
	}
	if( m_protocol != SOCKET_TCP )
	{
		throw Exception("Socket's protocol doesn't support accept(...) operation");
		return NULL;
	}

	unsigned int lenght;
	struct sockaddr_in address;

	int socketService = accept( m_socketId, ( struct sockaddr* ) &address, &lenght );

	returnSocket = new Socket( socketService, SOCKET_TCP );
	return returnSocket;
}

void Socket::writeData( NetworkByteArray * data ) throw ( Exception )
{

	if( ! m_isConnected )
	{
			throw Exception("Socket is not connected");
	}

	int bytesWritten = 0;


	int bytesLeft = data->getSize();

	cout<<"Taille = "<<bytesLeft<<endl;
	cout<<"Ecrit = "<<bytesWritten<<endl;
	cout<<"id_sock = "<<m_socketId<<endl;

	do
	{
			bytesWritten = write(
											m_socketId,
											&( data->getArrayPointer()[ data->getSize() - bytesLeft ] ),
											bytesLeft * sizeof( char )
											);

			if( bytesWritten == -1 )
			{
				throw Exception("Problème d'écriture");
			}
			else
			{
				cout << "Données parties=" << bytesWritten <<endl;
			}

			bytesLeft -= bytesWritten;

	}while( bytesLeft > 0 );

	cout<<"...envoyé"<<endl;
}

NetworkByteArray * Socket::readData( void ) throw ( Exception )
{
	if( ! m_isConnected )
	{
			throw Exception("Socket is not connected");
	}
	NetworkByteArray * data = new NetworkByteArray();
	int bytesRead;
	char c;
	do
	{
			bytesRead = read( m_socketId, &c, 1 * sizeof( char ) );

			if( bytesRead == -1 )
			{
				throw Exception("Problème de lecture");
			}

			if( bytesRead > 0 ){
				data->push( c );
			}

	}while( bytesRead > 0 );

	return data;
}
